# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         testcase.py
# Description:  
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2019-11-19
# -------------------------------------------------------------------------------
import json
import os
import time

from django.db import transaction
from django.http import StreamingHttpResponse
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status
from rest_framework.filters import OrderingFilter
from rest_framework.generics import DestroyAPIView
from rest_framework.mixins import UpdateModelMixin, CreateModelMixin
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet, GenericViewSet

from apps.BasicAuthService.models import Role
from apps.CeleryScheduledTaskService.HttpAutoTestService.testcase_task import batch_exec_testcase
from apps.HttpAutoTestService.core import utils
from apps.HttpAutoTestService.core.builtin.functions import get_timestamp
from apps.HttpAutoTestService.core.http_client import HttpSession
from apps.HttpAutoTestService.core.http_session_context import HttpSessionContext
from apps.HttpAutoTestService.core.http_testcase_debuger import HttpTestcaseDebuger
from apps.HttpAutoTestService.filters import TestcaseFilter
from apps.HttpAutoTestService.models import Testcase, Environment, Api, Testsuite2Testcase, TestcaseFolder, Module
from apps.HttpAutoTestService.serializers import TestcaseListSerializer, TestcaseSerializer
from apps.HttpAutoTestService.utils import file_reader
from utils.common import get_version
from utils.excel_util import ExcelReadUtil

__all__ = ['TestcaseListViewSet', 'TestcaseViewSet', 'TestcaseBatchDestroyAPIView', 'TestcaseDebugAPIView',
           'TestcaseBatchExecAPIView', 'TestcaseTemplateDownloadAPIView', 'TestcaseUploadAPIView',
           'TestcaseBatchCreateAPIView', 'TestcaseMoveAPIView']


class TestcaseListViewSet(ReadOnlyModelViewSet):
    """
    用例列表
    """
    serializer_class = TestcaseListSerializer
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    filter_class = TestcaseFilter
    ordering_fields = ['create_time', 'update_time']

    def get_queryset(self):
        user = self.request.user
        testcases = Testcase.objects.filter(is_deleted=False)
        if not Role.objects.filter(code='SUPERADMIN', user=user, is_deleted=False):
            api_ids = Api.objects.filter(is_deleted=False, project__members=user).values_list('id',
                                                                                              flat=True).distinct()
            testcases = testcases.filter(api_id__in=api_ids)
        return testcases


class TestcaseViewSet(CreateModelMixin, UpdateModelMixin, GenericViewSet):
    """
    用例创建、更新
    """
    queryset = Testcase.objects.filter(is_deleted=False)
    serializer_class = TestcaseSerializer

    def create(self, request, *args, **kwargs):
        user = request.user
        data = request.data
        name = data.get('name')
        if Testcase.objects.filter(name=name):
            data['name'] = '{name}-{timestamp}'.format(name=name, timestamp=str(get_timestamp(16))[10:])
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        testcase = serializer.save()
        testcase_folder = TestcaseFolder.objects.filter(project_id=testcase.api.project_id, is_system=True,
                                                        is_deleted=False).first()
        testcase.testcase_folder = testcase_folder
        testcase.creator = testcase.modifier = user
        testcase.save()
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def update(self, request, *args, **kwargs):
        data = request.data
        name = data.get('name', None)
        if name and Testcase.objects.filter(name=name) and not Testcase.objects.filter(id=data['id'], name=name):
            # 用例名称在数据库中已经存在，生成一个新用例名称
            data['name'] = '{name}-{timestamp}'.format(name=name, timestamp=str(get_timestamp(16))[10:])

        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        if getattr(instance, '_prefetched_objects_cache', None):
            instance._prefetched_objects_cache = {}
        return Response(serializer.data)


class TestcaseBatchDestroyAPIView(DestroyAPIView):
    """
    用例批量删除
    """

    def delete(self, request, *args, **kwargs):
        unbundle = request.data.get('unbundle')  # 解绑 true：删除用例并解绑与场景的关系，false：只删除未关联场景的用例
        testcase_ids = request.data.get('testcases')
        if unbundle:
            # 删除用例并解绑与场景的关系
            # 1、删除用例
            Testcase.objects.filter(id__in=testcase_ids).update(is_deleted=True)
            # 2、解绑用例与场景的关联
            Testsuite2Testcase.objects.filter(testcase_id__in=testcase_ids).delete()
        else:
            # 只删除未关联场景的用例
            Testcase.objects.filter(id__in=testcase_ids).exclude(
                id__in=Testsuite2Testcase.objects.filter(testcase_id__in=testcase_ids).values_list('testcase_id',
                                                                                                   flat=True).distinct()).update(
                is_deleted=True)
        return Response(status=status.HTTP_204_NO_CONTENT)


class TestcaseDebugAPIView(APIView):
    """
    用例调试
    """

    def post(self, request, *args, **kwargs):
        executor = self.request.user
        data = request.data
        # 接口传入的参数 environment、testcase
        environment_id = data.get('environment')
        testcase = data.get('testcase')
        name = testcase.get('name')
        api_id = testcase.get('api')
        cookies = testcase.get('cookies')
        headers = testcase.get('headers')
        request_params = testcase.get('request_params')
        request_data = testcase.get('request_data')
        request_data_type = testcase.get('request_data_type')
        expect_result = testcase.get('expect_result')

        # 1、获取接口信息
        api = Api.objects.filter(id=api_id, is_deleted=False).first()
        method = api.method.upper()

        # 2、获取运行环境
        environment = Environment.objects.filter(id=environment_id, is_deleted=False).first()
        base_url = environment.base_url
        url = utils.build_url(base_url=base_url, url=testcase.get("url"))
        if request_params:
            if '?' in url:
                url = "{url}&{params}".format(url=url.rstrip('&'), params=request_params.lstrip('&'))
            else:
                url = "{url}?{params}".format(url=url.rstrip('/'), params=request_params.lstrip('?'))

        # 3、获取项目全局参数
        project = api.project
        global_variables = project.global_variables or {}

        # 4、创建HttpSession对象实例
        http_session = HttpSession()

        # 5、创建HttpSessionContext对象实例
        http_session_context = HttpSessionContext(project_variables=global_variables)

        # 6、创建HttpTestcaseDebuger对象实例
        http_testcase_debuger = None
        if request_data_type == "Json":
            http_testcase_debuger = HttpTestcaseDebuger(http_session=http_session,
                                                        http_session_context=http_session_context, name=name, url=url,
                                                        method=method, cookies=cookies, headers=headers,
                                                        request_data_type=request_data_type, json_data=request_data,
                                                        expect_result=expect_result)
        elif request_data_type == "Form Data":
            http_testcase_debuger = HttpTestcaseDebuger(http_session=http_session,
                                                        http_session_context=http_session_context, name=name, url=url,
                                                        method=method, cookies=cookies, headers=headers,
                                                        request_data_type=request_data_type, form_data=request_data,
                                                        expect_result=expect_result)

        testcase_result = http_testcase_debuger.debug()

        testcase_result.update({
            "project": project.id,
            "project_name": project.name,
            "api": api.id,
            "api_name": api.name,
            "testcase": testcase.get('id'),
            "testcase_name": testcase.get('name'),
            "request_data_type": request_data_type,
            "executor": executor.id,
            "executor_real_name": executor.real_name,
            'is_periodictask': False
        })
        return Response(testcase_result, content_type='application/json')


class TestcaseBatchExecAPIView(APIView):
    """
    用例批量执行
    """

    def post(self, request, *args, **kwargs):
        user = request.user
        data = request.data
        testcases = data.get('testcases')
        version = get_version()
        # 以异步方式执行
        batch_exec_testcase.delay(testcases=testcases, executor_id=user.id, is_periodictask=False, version=version)
        # 以阻塞方式执行
        # batch_exec_testcase(testcases=testcases, executor_id=user.id, is_periodictask=False, version=version)
        return Response(status=status.HTTP_200_OK, data='程序正在后台运行中,请稍后查看结果……', content_type='application/json')


class TestcaseTemplateDownloadAPIView(APIView):
    """
    下载批量创建用例模板（此功能暂不再使用）
    此方法对应的页面功能按钮（导入用例）已禁用，原因为：导入用例通过在 HTTP接口 页面进行导入，不再从 用例|场景 页面导入
    """

    def get(self, request, *args, **kwargs):
        base_dir = os.path.dirname(os.path.dirname(__file__))
        testcase_template = os.path.join(os.path.join(base_dir, 'templates'), '批量创建用例模板.xlsx')
        response = StreamingHttpResponse(file_reader.read_file(testcase_template))
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Disposition'] = 'attachment;filename="批量创建用例模板.xlsx"'
        return response


class TestcaseUploadAPIView(APIView):
    """
    上传excel文件，并读取内容（此功能暂不再使用）
    此方法对应的页面功能按钮（导入用例）已禁用，原因为：导入用例通过在 HTTP接口 页面进行导入，不再从 用例|场景 页面导入
    """

    def post(self, request, *args, **kwargs):
        # 文件列表
        file = request.FILES.get('file')
        title = ['name', 'url', 'method', 'headers', 'request_data_type', 'request_data', 'expect_result', 'remark']
        excel_data = ExcelReadUtil(file_contents=file.read()).get_datas(start_row=1)
        excel_data = [dict(zip(title, item)) for item in excel_data]
        default_headers = json.dumps({"Content-Type": "application/json"}, ensure_ascii=False)
        default_expect_result = json.dumps(
            {"output": [], "validate": [{"check": "status_code", "expect": 200, "comparator": "eq"}]},
            ensure_ascii=False)
        results = []
        for item in excel_data:
            if item.get('name') != "" and item.get("url") != "":
                item['url'] = item.get('url').lstrip('/')
                if item.get('method') == "":
                    item['method'] = 'GET'
                if item.get('headers') == "":
                    item['headers'] = default_headers
                if item.get('request_data_type') == "":
                    item['request_data_type'] = 'Json'
                if item.get('request_data') == "":
                    item['request_data'] = '{}'
                if item.get('expect_result') == "":
                    item['expect_result'] = default_expect_result
                results.append(item)
        return Response(status=status.HTTP_200_OK, data=results, content_type='application/json')


class TestcaseBatchCreateAPIView(APIView):
    """
    批量创建用例（此功能暂不再使用）
    此方法对应的页面功能按钮（导入用例）已禁用，原因为：导入用例通过在 HTTP接口 页面进行导入，不再从 用例|场景 页面导入
    """

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        # 当前登录用户
        user = request.user
        # 用例所属项目id
        project = request.data.get('project')
        # 用例数据
        testcase_data = request.data.get('testcase')
        # 项目默认模块
        default_module = Module.objects.filter(project_id=project, is_system=True, is_deleted=False).first()
        # 项目默认用例夹
        default_testcase_folder = TestcaseFolder.objects.filter(project_id=project, is_system=True,
                                                                is_deleted=False).first()
        result = {
            'new_api': 0,
            'new_testcase': 0
        }
        for item in testcase_data:
            name = item.get('name')
            url = item_url = item.get('url').lstrip('/')
            request_params = ''
            index = item_url.find('?')
            if index != -1:
                url = item_url[:index]
                request_params = item_url[index + 1:]
            method = item.get('method').upper()
            headers = item.get('headers')
            request_data_type = item.get('request_data_type')
            request_data = item.get('request_data', '{}')
            expect_result = item.get('expect_result')
            remark = item.get('remark', '')
            api = Api.objects.filter(project_id=project, url=url, method=method, is_deleted=False).first()
            if not api:
                # 接口不存在，创建新接口
                api_dict = {
                    'name': name,
                    'url': url,
                    'method': method,
                    'headers': headers,
                    'request_data_type': request_data_type,
                    'request_params': request_params,
                    'request_data': request_data,
                    'remark': remark,
                    'project_id': project,
                    'module_id': default_module.id,
                    'creator_id': user.id,
                    'modifier_id': user.id
                }
                api = Api(**api_dict)
                api.save()
                result['new_api'] += 1

            if Testcase.objects.filter(name=name):
                time.sleep(0.1)
                name = '{name}-{timestamp}'.format(name=name, timestamp=str(get_timestamp(16))[10:])
            testcase_dict = {
                'name': name,
                'url': url,
                'headers': headers,
                'request_params': request_params,
                'request_data_type': request_data_type,
                'request_data': request_data,
                'expect_result': expect_result,
                'remark': remark,
                'api_id': api.id,
                'testcase_folder_id': default_testcase_folder.id,
                'creator_id': user.id,
                'modifier_id': user.id
            }
            testcase = Testcase(**testcase_dict)
            testcase.save()
            result['new_testcase'] += 1
        return Response(status=status.HTTP_200_OK, data=result, content_type='application/json')


class TestcaseMoveAPIView(APIView):
    """
    用例搬家
    """

    def post(self, request, *args, **kwargs):
        data = request.data
        testcase_folder = data.get('testcase_folder')
        testcases = data.get('testcases')
        if testcase_folder:
            Testcase.objects.filter(id__in=testcases).update(testcase_folder=testcase_folder)
        return Response(status=status.HTTP_200_OK, data='用例搬家成功……', content_type='application/json')
